01

您所在的位置:网站首页 grpc createchannel 01

01

2023-05-01 17:43| 来源: 网络整理| 查看: 265

2 Protocol Buffers编码介绍 2.1  Protocol Buffers编码格式

Protocol Buffers编码提供了一种灵活、高效、自动序列化结构数据的机制。Protocol Buffers与XML、JSON编码类似,不同之处在于Protocol Buffers是一种二进制编码,性能更高。

表2-1对比了Protocol Buffers和对应的JSON编码格式。

表2-1 Protocol Buffers和对应的JSON编码格式示例

Protocol Buffers编码

对应的JSON编码

{

1:“H3C”

2:“H3C”

3:“H3C device_test”

4:“Syslog/LogBuffer”

5:"notification": {

"Syslog": {

"LogBuffer": {

"BufferSize": 512,

"BufferSizeLimit": 1024,

"DroppedLogsCount": 0,

"LogsCount": 100,

"LogsCountPerSeverity": {

"Alert": 0,

"Critical": 1,

"Debug": 0,

"Emergency": 0,

"Error": 3,

"Informational": 80,

"Notice": 15,

"Warning": 1

},

"OverwrittenLogsCount": 0,

"State": "enable"

}

},

"Timestamp": "1527206160022"

}

}

{

"producerName": "H3C",

"deviceName": "H3C",

"deviceModel": "H3C device_test",

"sensorPath": "Syslog/LogBuffer",

"jsonData": {

"notification": {

"Syslog": {

"LogBuffer": {

"BufferSize": 512,

"BufferSizeLimit": 1024,

"DroppedLogsCount": 0,

"LogsCount": 100,

"LogsCountPerSeverity": {

"Alert": 0,

"Critical": 1,

"Debug": 0,

"Emergency": 0,

"Error": 3,

"Informational": 80,

"Notice": 15,

"Warning": 1

},

"OverwrittenLogsCount": 0,

"State": "enable"

}

},

"Timestamp": "1527206160022"

}

}

}

 

2.2  proto文件介绍

Protocol Buffers编码通过proto文件描述数据结构,用户可以利用Protoc等工具软件根据proto文件自动生成其他编程语言(例如Java、C++)代码,然后基于这些生成的代码进行二次开发,以实现gRPC设备对接。

H3C为Dial-in模式和Dial-out模式分别提供了proto文件。

2.2.1  Dial-in模式的proto文件 1. 公共proto文件

Dial-in模式的公共proto文件包括:

·     grpc_service.proto:定义了Dial-in模式下的公共RPC方法。

·     gnmi.proto:定义了gNMI类操作的公共RPC方法。

·     gnmi_ext.proto:定义了gnmi.proto文件所需的扩展消息结构。

其中gnmi.proto和gnmi_ext.proto文件由Google发布,下载地址请参见“2.2.3  获取proto文件的方法”。

grpc_service.proto文件由H3C提供,其内容和含义如下:

syntax = "proto2";

package grpc_service;

message GetJsonReply { //Get方法应答结果

    required string result = 1;

}

message SubscribeReply { //订阅结果

    required string result = 1;

}

message ConfigReply { //配置结果

    required string result = 1;

}

message ReportEvent { //订阅事件结果定义

    required string token_id = 1; //登录token_id

    required string stream_name = 2; //订阅的事件流名称

    required string event_name = 3; //订阅的事件名

    required string json_text = 4; //订阅结果json字符串

}

message GetReportRequest{ //获取事件订阅结果请求

    required string token_id = 1; //登录成功后的token_id

}

message LoginRequest {  //登录请求参数定义

    required string user_name = 1; //登录请求用户名

    required string password = 2; //登录请求密码

}

message LoginReply {  //登录请求应答定义

    required string token_id = 1; //登录成功后返回的token_id

}

message LogoutRequest { //退出登录请求参数定义

    required string token_id = 1; //token_id

}

message LogoutReply { //退出登录返回结果定义

    required string result = 1; //退出登录结果

}

message SubscribeRequest { //定义事件流名称

    required string stream_name = 1;

}

message CliConfigArgs { //向设备下发配置命令,并指定命令行参数

    required int64 ReqId = 1; //配置命令请求ID

    required string cli = 2;  //配置命令

}

message CliConfigReply { //设备返回配置命令行执行的结果

    required int64 ResReqId = 1; //返回配置命令请求ID,与CliConfigArgs相对应

  required string output = 2;    //返回配置命令执行输出

    required string errors = 3;  //标记配置命令执行结果

}

message DisplayCmdArgs { //向设备下发display命令,并指定命令行参数

    required int64 ReqId = 1; //display命令请求ID

    required string cli = 2;  //display命令

}

message DisplayCmdReply { //设备返回display命令行执行的结果

    required int64 ResReqId =1;  //display命令请求ID,与DisplayCmdArgs相对应

    required string output = 2;  //返回display命令执行输出

    required string errors = 3;  //标记display命令执行结果

}

service GrpcService { //定义gRPC方法

    rpc Login (LoginRequest) returns (LoginReply) {}  //登录方法

    rpc Logout (LogoutRequest) returns (LogoutReply) {} //退出登录方法

    rpc SubscribeByStreamName (SubscribeRequest) returns (SubscribeReply) {} //订阅事件流

    rpc GetEventReport (GetReportRequest) returns (stream ReportEvent) {} //获取事件结果

    rpc CliConfig (CliConfigArgs)  returns (stream CliConfigReply) {} //gRPC支持通过命令行下发配置命令,并返回执行结果

    rpc DisplayCmdTextOutput(DisplayCmdArgs)  returns(stream DisplayCmdReply) {} //gRPC支持通过命令行下发display命令,并返回查询结果

}

2. 业务模块proto文件

Dial-in模式支持Device、Ifmgr、IPFW、LLDP、Syslog等业务模块proto文件。

以Device.proto文件为例,该文件定义了Device模块数据的RPC方法,其内容和含义如下:

syntax = "proto2";

import "grpc_service.proto";

package  device;

message DeviceBase { //获取设备基本信息结构定义

    optional string HostName = 1; //设备的名称

    optional string HostOid = 2; //sysoid

    optional uint32 MaxChassisNum = 3; //最大框数

    optional uint32 MaxSlotNum = 4; //最大slot数

    optional string HostDescription = 5; //设备描述信息

}

message DevicePhysicalEntities { //设备物理实体信息

    message Entity {

        optional uint32 PhysicalIndex = 1; //实体索引

        optional string VendorType = 2; //vendor类型

        optional uint32 EntityClass = 3;//实体类型

        optional string SoftwareRev = 4; //软件版本

        optional string SerialNumber = 5; //序列号

        optional string Model = 6; //模式

    }

    repeated Entity entity = 1;

}

service DeviceService { //定义的RPC方法

    rpc GetJsonDeviceBase(DeviceBase) returns (grpc_service.GetJsonReply) {} //获取设备基本信息

    rpc GetJsonDevicePhysicalEntities(DevicePhysicalEntities) returns (grpc_service.GetJsonReply) {} //获取设备实体信息

}

2.2.2  Dial-out模式的proto文件

Dial-out模式的公共proto文件包括:

·     grpc_dialout.proto:定义了Dial-out模式下普通订阅的公共RPC方法。

·     dial_out.proto:定义了Dial-out模式下gNMI模式订阅的报文格式。

·     gnmi.proto:定义了gNMI模式订阅的公共RPC方法。

·     gnmi_ext.proto:定义了gnmi.proto文件所需的扩展消息结构。

其中dial_out.proto文件由SONIC发布,gnmi.proto和gnmi_ext.proto文件由Google发布,下载地址请参见“2.2.3  获取proto文件的方法”。

grpc_dialout.proto文件由H3C提供,其内容和含义如下:

syntax = "proto2";

package grpc_dialout;

message DeviceInfo{ //推送的设备信息

    required string producerName = 1; //厂商名

    required string deviceName = 2; //设备的名称

    required string deviceModel = 3; //设备型号

}

message DialoutMsg{   //推送的消息格式描述

    required DeviceInfo deviceMsg = 1; //DeviceInfo所描述的设备信息

    required string sensorPath = 2; //采样路径,对应netconf表的xpath路径

    required string jsonData = 3; //采样结果数据(JSON格式字符串)

}

message DialoutResponse{  //采集器(gRPC服务器)返回信息,预留(暂不处理返回值,可填充任意值)

    required string response = 1;

}

service GRPCDialout {  //推送方法

    rpc Dialout(stream DialoutMsg) returns (DialoutResponse);

}

2.2.3  获取proto文件的方法

可联系H3C技术支持提供proto文件。

gnmi.proto和gnmi_ext.proto文件可从以下网站下载:

·     https://github.com/openconfig/gnmi/tree/master/proto/gnmi/gnmi.proto

·     https://github.com/openconfig/gnmi/tree/master/proto/gnmi_ext/gnmi_ext.proto

dial-out.proto文件可从以下网站下载:

·     https://github.com/Azure/sonic-telemetry/blob/master/proto/dial_out.proto

2.3  gRPC对接软件二次开发举例

本举例开发的软件用于实现:

·     采集器获取设备数据(Dial-in模式下的Get操作、gNMI Capabilities操作、gNMI Get操作、gNMI Subscribe操作或Dial-out模式)

·     采集器向设备下发配置(Dial-in模式下的gNMI Set操作或CLI操作)

开发环境为Linux,编程语言以C++为例。

2.3.1  准备工作

(1)     获取proto文件:

¡     Dial-in模式下的Get操作:需要grpc_service.proto文件和具体业务模块对应的proto文件。

¡     Dial-in模式下的gNMI类操作:需要grpc_service.proto、gnmi.proto和gnmi_ext.proto文件。

¡     Dial-in模式下的CLI操作:需要grpc_service.proto文件。

¡     Dial-out模式下的普通订阅:需要grpc_dialout.proto文件。

¡     Dial-out模式下的gNMI模式订阅:需要dial_out.proto、gnmi.proto和gnmi_ext.proto文件。

(2)     获取处理proto文件的工具软件protoc。

下载地址:https://github.com/google/protobuf/releases

(3)     获取对应开发语言的protobuf插件,例如C++插件protobuf-cpp。

下载地址:https://github.com/google/protobuf/releases

2.3.2  生成代码

生成proto文件对应的C++代码。

1. Dial-in模式

将需要的proto文件收集到当前目录下,例如grpc_service.proto和BufferMonitor.proto。

$protoc --plugin=./grpc_cpp_plugin  --grpc_out=. --cpp_out=. *.proto

2. Dial-out模式

将grpc_dialout.proto文件收集到当前目录下。

$ protoc --plugin=./grpc_cpp_plugin  --grpc_out=.  --cpp_out=. *.proto

2.3.3  开发代码(Dial-in模式)

对于Dial-in模式,主要是实现gRPC客户端代码(采集器上使用)。

由于生成的proto代码已经封装好了对应的服务类,只要在编写的客户端代码中调用其RPC方法,客户端就能够向设备(gRPC服务器)发起对应的RPC请求。

客户端代码主要包括以下3部分:

·     进行登录操作,获取token_id。

·     为要发起的RPC方法准备参数,用proto生成的服务类发起RPC调用并解析返回结果。

·     退出登录。

1. Get操作

以调用GrpcService和BufferMonitorService服务类为例,编码步骤如下:

(1)     编写一个GrpcServiceTest类。

在这个类中使用由grpc_service.proto生成的GrpcService::Stub类,通过grpc_service.proto自动生成的Login和Logout方法分别完成登录和退出。

class GrpcServiceTest

{

public:

    /* 构造函数 */

GrpcServiceTest(std::shared_ptr channel): GrpcServiceStub(GrpcService::NewStub(channel))  {}

 

    /* 成员函数 */

    int Login(const std::string& username, const std::string& password);

void Logout();

void listen();

Status listen(const std::string& command);

 

    /* 成员变量 */

std::string token;

 

private:

std::unique_ptr GrpcServiceStub;  //使用grpc_service.proto生成的GrpcService::Stub类

};

(2)     实现自定义的Login方法。

通过用户输入的用户名,密码调用GrpcService::Stub类的Login方法完成登录。

int GrpcServiceTest::Login(const std::string& username, const std::string& password)

{

    LoginRequest request;   //设置用户名密码

    request.set_user_name(username);

    request.set_password(password);

 

LoginReply reply;

ClientContext context;

 

//调用登录方法

Status status = GrpcServiceStub->Login(&context, request, &reply);

if (status.ok())

    {

     std::cout add_elem();

  pathelem03->set_name("Neighbor");

  (*pathelem03->mutable_key())["IfName"] = "xxx";

 

subscribeList->set_mode(::gnmi::SubscriptionList_Mode_ONCE);

subscribeList->set_encoding(::gnmi::JSON);

}

 

void gnmi_client::FillSubscribeRequestByPool(gnmi::SubscribeRequest &request)

{

auto subscribeList =  request.mutable_subscribe();

 

auto prefix = subscribeList->mutable_prefix();

auto pathelem01 = prefix->add_elem();

pathelem01->set_name("Device");

 

auto subscribe = subscribeList->add_subscription();

auto path = subscribe->mutable_path();

auto pathelem02 = path->add_elem();

pathelem02->set_name("CPUs");

auto pathelem03 = path->add_elem();

pathelem03->set_name("CPU");

auto pathelem04 = path->add_elem();

pathelem04->set_name("CPUUsage");

 

subscribeList->set_mode(::gnmi::SubscriptionList_Mode_POLL);

subscribeList->set_encoding(::gnmi::JSON);

}

 

void gnmi_client::FillSubscribeRequestByStream(gnmi::SubscribeRequest &request)

{

 auto subscribeList =  request.mutable_subscribe();

 

 auto prefix = subscribeList->mutable_prefix();

 auto pathelem01 = prefix->add_elem();

 pathelem01->set_name("Diagnostic");

 

 auto subscribe = subscribeList->add_subscription();

 

 auto path = subscribe->mutable_path();

 auto pathelem02 = path->add_elem();

 pathelem02->set_name("CPUEvent");

 auto pathelem03 = path->add_elem();

 pathelem03->set_name("CPU");

 (*pathelem03->mutable_key())["Chassis#condition"] = "equal:1";

 subscribe->set_mode(::gnmi::ON_CHANGE);

 subscribe->set_sample_interval(1000);

 subscribe->set_suppress_redundant(false);

 subscribe->set_heartbeat_interval(1000);

 

 subscribeList->set_mode(::gnmi::SubscriptionList_Mode_STREAM);

 subscribeList->set_encoding(::gnmi::JSON);

}

 

void gnmi_client::FillSubscribeAlias(gnmi::SubscribeRequest &request)

{

auto aliases = request.mutable_aliases();

auto alias = aliases->add_alias();

 

auto path = alias->mutable_path();

auto pathelem01 = path->add_elem();

pathelem01->set_name("Device");

auto pathelem02 = path->add_elem();

pathelem02->set_name("CPUs");

auto pathelem03 = path->add_elem();

pathelem03->set_name("CPU");

auto pathelem04 = path->add_elem();

pathelem04->set_name("CPUUsage");

 

alias->set_alias("#cpu_usage");

}

(6)     调用Logout退出登录。

与Get操作相同。

6. CLI操作

(1)     编写一个GrpcServiceTest类。

与Get操作相同。

(2)     实现自定义的Login方法。

与Get操作相同。

(3)     发起对设备的RPC方法请求。

这里使用grpc_service.proto文件中的CliConfig方法:

rpc CliConfig (CliConfigArgs)  returns (stream CliConfigReply) {}

(4)     使用GrpcServiceTest类封装发起的RPC请求。

见Get操作中的GrpcServiceTest类。

(5)     实现自定义的方法以支持CliConfig操作。

//  make a thread to listen the sever and get message

Status GrpcServiceTest::listen(const std::string& command)

{

CliConfigArgs reportRequest;

ClientContext context;

CliConfigReply reportedEvent;

std::string key = "token_id";

std::string value = token;

context.AddMetadata(key, value);

/* add token to request */

reportRequest.set_reqid(12345678);

reportRequest.set_cli(command);

 

std::unique_ptr< ClientReader< CliConfigReply>> reader(mStub->CliConfig(&context, reportRequest));

std::string streamName; 

std::string output; 

int64 resreqid;

 

std::cout add_elem();

 pathelem03->set_name("Entity");

 auto pathelem04 = path1->add_elem();

 pathelem04->set_name("CpuUsage");

 (*pathelem04->mutable_key())["#match"] = "more:50";

·     valuetype(属性值类型)

获取接口数据时,如果IfIndex、vrfindex列的值为数字,设备无法识别该值为名称类型还是索引类型。此时,用户可以使用valuetype指定该值的类型。valuetype取值为:

属性

说明

name

值为名称类型

index

值为索引类型

auto

设备先按名称类型进行匹配,如果没有匹配到任何信息,再按照索引类型进行匹配

如果不指定valuetype的属性,缺省使用auto

 

如下示例中,IfIndex属性值为1,属性值类型为index:

auto prefix = request.mutable_prefix();

 auto pathelem01 = prefix->add_elem();

 pathelem01->set_name("VLAN");

 

 auto path1 = request.add_path();

 auto pathelem02 = path1->add_elem();

 pathelem02->set_name("TrunkInterfaces");

 auto pathelem03 = path1->add_elem();

 pathelem03->set_name("Interface");

(*pathelem03->mutable_key())["IfIndex"] = "1";

(*pathelem03->mutable_key())["IfIndex#valuetype"] = "index";

·     count

该属性用于获取指定数量的数据项,例如运行状态、运行配置信息。

如下为一个携带了count属性的示例:

auto prefix = request.mutable_prefix();

 auto pathelem01 = prefix->add_elem();

 pathelem01->set_name("Syslog");

 

 auto path1 = request.add_path();

 auto pathelem02 = path1->add_elem();

 pathelem02->set_name("Logs");

(*pathelem03->mutable_key())["count"] = "5";

 auto pathelem03 = path1->add_elem();

 pathelem03->set_name("Log");

(*pathelem03->mutable_key())["Index"] = "10";

·     值过滤

在Get操作中,对表中的列直接指定值,设备将对这些值进行严格匹配。如果指定了多个列的值,则返回同时符合这几个条件的数据。

如下为匹配IfIndex=1的示例:

auto prefix = request.mutable_prefix();

 auto pathelem01 = prefix->add_elem();

 pathelem01->set_name("VLAN");

 

 auto path1 = request.add_path();

 auto pathelem02 = path1->add_elem();

 pathelem02->set_name("TrunkInterfaces");

 auto pathelem03 = path1->add_elem();

 pathelem03->set_name("Interface");

(*pathelem03->mutable_key())["IfIndex"] = "1";

3. gNMI Set操作支持的属性

gNMI Set支持的属性如下:

·     incremental

该属性作用于集合性质的列,例如vlan permitlist列表。XML请求中有增量下发选项时,最终执行结果不影响本列原有的数据。

例如,下发一个接口的VLAN配置,使用增量下发,262接口原有Untagged VLAN列表为12~15,下发后为1~10,12~15。请求如下:

Path     *path01 = request.mutable_prefix();

PathElem *pathelem01 = path01->add_elem();  

pathelem01->set_name("VLAN");   

PathElem *pathelem02 = path01->add_elem();

pathelem02->set_name("HybridInterfaces");

Update   *Update01 = request.add_update();

Path     *path02 = Update01->mutable_path();

PathElem *pathelem03 = path02->add_elem();

pathelem03->set_name("Interface");

(*pathelem03->mutable_key())["IfName"] = "262";

(*pathelem03->mutable_key())["UntaggedVlanList#incremental"] = "true";

(*pathelem03->mutable_key())["UntaggedVlanList"] = "1-10";

·     valuetype

配置接口数据时,如果IfIndex和vrfindex列的值为数字,设备无法识别该值为名称类型还是索引类型。此时,用户可以使用valuetype指定该值的类型。valuetype取值为:

属性

说明

name

值为名称类型

index

值为索引类型

auto

设备先按名称类型进行匹配,如果没有匹配到任何信息,再按照索引类型进行匹配

如果不指定valuetype的属性,缺省使用auto

 

下面以IfIndex列值为index类型,值为1为例:

Path        *path01 = request.mutable_prefix();

PathElem    *pathelem01 = path01->add_elem();  

pathelem01->set_name("VLAN");  

PathElem    *pathelem02 = path01->add_elem();

pathelem02->set_name("TrunkInterfaces");

 

/* SetRequest->delete */

Path        *path02 = request.add_delete_();   

PathElem    *pathelem04 = path02->add_elem();

pathelem04->set_name("Interface ");

(*pathelem04->mutable_key())["IfIndex"] = "1";

(*pathelem04->mutable_key())["IfIndex#valuetype"] = "index";

4. gNMI Subscribe操作支持的属性

gNMI Subscribe支持的属性如下:

·     gNMI Get操作支持的所有属性,请参见“gNMI Get操作支持的属性”

这些属性可用于周期式上报的数据表的订阅。

·     condition(数据推送条件)

该属性用于事件表的订阅,可选择的属性值包括:

¡     more:大于

¡     less:小于

¡     notLess:不小于

¡     notMore:不大于

¡     equal:等于

¡     notEqual:不等于

¡     include:包含

¡     exclude:不包含

¡     startWith:开始于

¡     endWith:结束于

如下示例用于监控LLDP事件:

  auto prefix = subscribeList->mutable_prefix();

  auto pathelem01 = prefix->add_elem();

  pathelem01->set_name("LLDP");

 

  auto subscribe = subscribeList->add_subscription();

  auto path = subscribe->mutable_path();

  auto pathelem02 = path->add_elem();

  pathelem02->set_name("NeighborEvent");

  auto pathelem03 = path->add_elem();

  pathelem03->set_name("Neighbor");

  (*pathelem03->mutable_key())["IfName"] = "Ten-GigabitEthernet1/0/1";

  (*pathelem03->mutable_key())["IfName#condition"] = "equal";

·     match(条件匹配)

该属性用于数据表的订阅,有以下两种使用方法:

¡     与固定值进行条件匹配:支持的匹配命令如表2-3所示。

¡     与其他列数据进行条件匹配(仅触发式上报的数据表支持)。可选择的属性值包括:

-     refMore:大于

-     refLess:小于

-     refNotLess:不小于

-     refNotMore:不大于

-     refEqual:等于

-     refNotEqual:不等于

下面以current-usage列值大于alarm-threshold列值触发条件为例:

  auto subscribe = subscribeList->add_subscription();

  auto path = subscribe->mutable_path();

  auto pathelem02 = path->add_elem();

  pathelem02->set_name("queue");

  auto pathelem03 = path->add_elem();

  pathelem03->set_name("state");

  (*pathelem03->mutable_key())["current-usage#match"] = "refMore:alarm-threshold";

·     select

该属性用于在触发式上报的数据表的订阅中选择列,表示仅订阅指定的列数据。

下面以订阅current-usage和available-usage为例:

  auto subscribe = subscribeList->add_subscription();

  auto path = subscribe->mutable_path();

  auto pathelem02 = path->add_elem();

  pathelem02->set_name("queue");

  auto pathelem03 = path->add_elem();

  pathelem03->set_name("state");

  (*pathelem03->mutable_key())["current-usage#select"] = "";

  (*pathelem03->mutable_key())["available-usage#select"] = "";

说明

gNMI类操作对象包括数据表和事件表:

·     数据表有两种类型:周期式上报和触发式上报。

·     事件表均为触发式上报。

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3